﻿using System;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
using NeuronDotNet.Core;
using NeuronDotNet.Core.Backpropagation;
using NeuronDotNet.Core.Initializers;
using ZedGraph;

namespace NeuronDotNet.Samples.FunctionApproximation
{
    class MyNetwork
    {
        private double learningRate;
        private int neuronCount;
        private int cycles;
        private BackpropagationNetwork network;
        private List<double> data = new List<double>();

        public MyNetwork(double learningRate, int neuronCount, int cycles) 
        {
            this.learningRate = learningRate;
            this.neuronCount = neuronCount;
            this.cycles = cycles;

            LinearLayer inputLayer = new LinearLayer(2);
            SigmoidLayer hiddenLayer = new SigmoidLayer(neuronCount);
            SigmoidLayer outputLayer = new SigmoidLayer(1);
            new BackpropagationConnector(inputLayer, hiddenLayer).Initializer = new RandomFunction(0d, 0.3d);
            new BackpropagationConnector(hiddenLayer, outputLayer).Initializer = new RandomFunction(0d, 0.3d);
            network = new BackpropagationNetwork(inputLayer, outputLayer);
            network.SetLearningRate(learningRate);
        }

        public void StartLearning(TextBox textBoxDby, TextBox textBoxY, System.Windows.Forms.Label Mout, ProgressBar trainingProgressBar)
        {
            TrainingSet trainingSet = new TrainingSet(2, 1);

            data = GetTrainingData();

            if (data.Count > 3)
            {
                for (int i = 0; i < data.Count - 3; i++)
                {
                    trainingSet.Add(new TrainingSample(new double[] { MyNormalize(data[i]), MyNormalize(data[i + 1]) }, new double[] { MyNormalize(data[i + 3]) }));
                }
                network.EndEpochEvent += new TrainingEpochEventHandler(
                    delegate(object senderNetwork, TrainingEpochEventArgs args)
                    {
                        trainingProgressBar.Value = (int)(args.TrainingIteration * 100d / cycles);
                        Application.DoEvents();
                    });
                network.Learn(trainingSet, cycles);
                StopLearning(textBoxDby, textBoxY, Mout);
            }
            else
            {
                MessageBox.Show("Not enough training data. Check \"in\" file");
            }

        }

        public void StopLearning(TextBox textBoxDby, TextBox textBoxY, System.Windows.Forms.Label Mout)
        {
            if (network != null)
            {
                network.StopLearning();
                double a = Double.Parse(textBoxDby.Text);
                double b = Double.Parse(textBoxY.Text);
                double[] moutarr = network.Run(new double[] { MyNormalize(a), MyNormalize(b) });
                Mout.Text = String.Format("{0:0.0000}", MyDenormalize(moutarr[0]));
            }
            network = null;
            //EnableControls(true);
        }


    /*
     * Файл должен находиться в той же дериктории, что и .exe 
     * Структура файла in:
     * данные вводятся по возрастанию даты. 
     * то есть самая последняя запись - самая новая.
     * необходимо вводить данные за каждый день.
     */

        private List<double> GetTrainingData()
        {
            List<double> data = new List<double>();

            try
            {
                string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

                string file = dir + @"\in";
                StreamReader reader = new StreamReader(file);
                string line = String.Empty;
                while ((line = reader.ReadLine()) != null)
                {
                    data.Add(Double.Parse(line));
                    Console.WriteLine(line);
                }
                reader.Close();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }

            return data;
        }

        private double MyNormalize(double value)
        {
            return value / GetNormalizer(data); // == value / avg / 10
        }

        private double MyDenormalize(double value)
        {
            return value * GetNormalizer(data);
        }

        private double GetNormalizer(List<double> data)
        {
            double min = Double.MaxValue;
            double max = 0;

            foreach (double d in data)
            {
                min = min > d ? d : min;
                max = max < d ? d : max;

            }

            return (min + max) * 5;

        }

    }
}
